package com.example.roundcornerprogressbar.view.progressbar;

import com.example.roundcornerprogressbar.ResourceTable;
import com.example.roundcornerprogressbar.view.progressbar.common.AnimatedRoundCornerProgressBar;
import ohos.agp.components.*;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.utils.Color;
import ohos.app.Context;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.utils.Parcel;
import ohos.utils.Sequenceable;

import static ohos.agp.components.AttrHelper.vp2px;

public class TextRoundCornerProgressBar extends AnimatedRoundCornerProgressBar implements ComponentTreeObserver.GlobalLayoutListener {
    protected final static int DEFAULT_TEXT_SIZE = 40;
    protected final static int DEFAULT_TEXT_MARGIN = 16;
    public final static int GRAVITY_START = 0;
    public final static int GRAVITY_END = 1;
    public final static int PRIORITY_INSIDE = 0;
    public final static int PRIORITY_OUTSIDE = 1;


    private Text tvProgress;
    private int colorTextProgress;
    private int textProgressSize;
    private int textProgressMargin;
    private String textProgress;
    private int textInsideGravity;
    private int textOutsideGravity;
    private int textPositionPriority;

    public TextRoundCornerProgressBar(Context context) {
        super(context);
    }

    public TextRoundCornerProgressBar(Context context, AttrSet attrSet) {
        super(context, attrSet);
    }

    public TextRoundCornerProgressBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
    }

    @Override
    protected int initLayout() {
        return ResourceTable.Layout_layout_text_round_corner_progress_bar;
    }

    @Override
    protected void initStyleable(Context context, AttrSet attrSet) {
        colorTextProgress = Color.WHITE.getValue();

        textProgressSize = vp2px(DEFAULT_TEXT_SIZE, context);
        textProgressMargin= vp2px(DEFAULT_TEXT_MARGIN, context);

        textInsideGravity = GRAVITY_START;
        textOutsideGravity = GRAVITY_START;
        textPositionPriority = PRIORITY_INSIDE;

    }

    @Override
    protected void initView() {
        tvProgress = (Text)findComponentById(ResourceTable.Id_tv_progress);
        tvProgress.getComponentTreeObserver().addTreeLayoutChangedListener(this);
    }

    @Override
    protected void drawProgress(DirectionalLayout layoutProgress,
                                ShapeElement progressDrawable,
                                float max,
                                float progress,
                                float totalWidth,
                                int radius,
                                int padding,
                                boolean isReverse) {
        int newRadius = radius - (padding / 2);
        progressDrawable.setCornerRadiiArray(new float[]{newRadius, newRadius, newRadius, newRadius, newRadius, newRadius, newRadius, newRadius});
        layoutProgress.setBackground(progressDrawable);

        float ratio = max / progress;
        int progressWidth = (int) ((totalWidth - (padding * 2)) / ratio);
        ComponentContainer.LayoutConfig progressParams = (ComponentContainer.LayoutConfig) layoutProgress.getLayoutConfig();
        if (padding + (progressWidth / 2) < radius) {
            int margin = Math.max(radius - padding, 0) - (progressWidth / 2);
            progressParams.setMarginTop(margin);
            progressParams.setMarginBottom(margin);
        } else {
            progressParams.setMarginTop(0);
            progressParams.setMarginBottom(0);
        }
        progressParams.width = progressWidth;
        layoutProgress.setLayoutConfig(progressParams);
    }

    @Override
    protected void onViewDraw() {
        drawTextProgress();
        drawTextProgressSize();
        drawTextProgressMargin();
        // Can't instantly change the text position of child view
        // when `onSizeChanged(...)` called. Using `post` method then
        // call these methods inside the Runnable will solved this.
        TaskDispatcher dispatcher = getContext().getUITaskDispatcher();
        dispatcher.asyncDispatch(new Runnable() {
            @Override
            public void run() {
                drawTextProgressPosition();
            }
        });

        drawTextProgressColor();
    }

    private void drawTextProgress() {
        tvProgress.setText(textProgress);
    }

    private void drawTextProgressColor() {
        tvProgress.setTextColor(new Color(colorTextProgress));
    }

    private void drawTextProgressSize() {
        tvProgress.setTextSize(textProgressSize, Text.TextSizeType.PX);
    }

    private void drawTextProgressMargin() {
        ComponentContainer.LayoutConfig params = (ComponentContainer.LayoutConfig) tvProgress.getLayoutConfig();
        params.setMargins(textProgressMargin, 0, textProgressMargin, 0);
        tvProgress.setLayoutConfig(params);
    }

    private void drawTextProgressPosition() {
        clearTextProgressAlign();
        int textProgressWidth = tvProgress.getWidth() + (getTextProgressMargin() * 2);
        float ratio = getMax() / getProgress();
        int progressWidth = (int) ((getLayoutWidth() - (getBasePadding() * 2)) / ratio);
        if (textPositionPriority == PRIORITY_OUTSIDE) {
            if (getLayoutWidth() - progressWidth > textProgressWidth) {
                alignTextProgressOutsideProgress();
            } else {
                alignTextProgressInsideProgress();
            }
        } else {
            if (textProgressWidth + textProgressMargin > progressWidth) {
                alignTextProgressOutsideProgress();
            } else {
                alignTextProgressInsideProgress();
            }
        }
    }

    private void clearTextProgressAlign() {
        DependentLayout.LayoutConfig params = (DependentLayout.LayoutConfig) tvProgress.getLayoutConfig();
        params.removeRule(DependentLayout.LayoutConfig.LEFT_OF);
        params.removeRule(DependentLayout.LayoutConfig.RIGHT_OF);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_LEFT);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_RIGHT);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_PARENT_LEFT);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_PARENT_RIGHT);
        params.removeRule(DependentLayout.LayoutConfig.START_OF);
        params.removeRule(DependentLayout.LayoutConfig.END_OF);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_START);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_END);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_PARENT_START);
        params.removeRule(DependentLayout.LayoutConfig.ALIGN_PARENT_END);

        tvProgress.setLayoutConfig(params);
    }

    private void alignTextProgressInsideProgress() {
        DependentLayout.LayoutConfig params = (DependentLayout.LayoutConfig) tvProgress.getLayoutConfig();
        if (isReverse()) {
            if (textInsideGravity == GRAVITY_END) {
                params.addRule(DependentLayout.LayoutConfig.ALIGN_RIGHT, ResourceTable.Id_layout_progress);
                params.addRule(DependentLayout.LayoutConfig.ALIGN_END, ResourceTable.Id_layout_progress);

            } else {
                params.addRule(DependentLayout.LayoutConfig.ALIGN_LEFT, ResourceTable.Id_layout_progress);
                params.addRule(DependentLayout.LayoutConfig.ALIGN_START, ResourceTable.Id_layout_progress);
            }
        } else {
            if (textInsideGravity == GRAVITY_END) {
                params.addRule(DependentLayout.LayoutConfig.ALIGN_LEFT, ResourceTable.Id_layout_progress);
                params.addRule(DependentLayout.LayoutConfig.ALIGN_START, ResourceTable.Id_layout_progress);
            } else {
                params.addRule(DependentLayout.LayoutConfig.ALIGN_RIGHT, ResourceTable.Id_layout_progress);
                params.addRule(DependentLayout.LayoutConfig.ALIGN_END, ResourceTable.Id_layout_progress);
            }
        }
        tvProgress.setLayoutConfig(params);
    }

    private void alignTextProgressOutsideProgress() {
        DependentLayout.LayoutConfig params = (DependentLayout.LayoutConfig) tvProgress.getLayoutConfig();
        if (isReverse()) {
            if (textOutsideGravity == GRAVITY_END) {
                params.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_LEFT);
                params.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_START);
            } else {
                params.addRule(DependentLayout.LayoutConfig.LEFT_OF, ResourceTable.Id_layout_progress);
                params.addRule(DependentLayout.LayoutConfig.START_OF, ResourceTable.Id_layout_progress);
            }
        } else {
            if (textOutsideGravity == GRAVITY_END) {
                params.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_RIGHT);
                params.addRule(DependentLayout.LayoutConfig.ALIGN_PARENT_END);
            } else {
                params.addRule(DependentLayout.LayoutConfig.RIGHT_OF, ResourceTable.Id_layout_progress);
                params.addRule(DependentLayout.LayoutConfig.END_OF, ResourceTable.Id_layout_progress);
            }
        }
        tvProgress.setLayoutConfig(params);
    }

    public String getProgressText() {
        return textProgress;
    }

    public void setProgressText(String text) {
        textProgress = text;
        drawTextProgress();
        drawTextProgressPosition();
    }

    @Override
    public void setProgress(int progress) {
        setProgress((float) progress);
    }

    @Override
    public void setProgress(float progress) {
        super.setProgress(progress);
        drawTextProgressPosition();
    }

    public int getTextProgressColor() {
        return colorTextProgress;
    }

    public void setTextProgressColor(int color) {
        this.colorTextProgress = color;
        drawTextProgressColor();
    }

    public int getTextProgressSize() {
        return textProgressSize;
    }

    public void setTextProgressSize(int size) {
        this.textProgressSize = size;
        drawTextProgressSize();
        drawTextProgressPosition();
    }

    public int getTextProgressMargin() {
        return textProgressMargin;
    }

    public void setTextProgressMargin(int margin) {
        this.textProgressMargin = margin;
        drawTextProgressMargin();
        drawTextProgressPosition();
    }

    public int getTextPositionPriority() {
        return textPositionPriority;
    }

    public void setTextPositionPriority(int priority) {
        this.textPositionPriority = priority;
        drawTextProgressPosition();
    }

    public int getTextInsideGravity() {
        return textInsideGravity;
    }

    public void setTextInsideGravity(int gravity) {
        this.textInsideGravity = gravity;
        drawTextProgressPosition();
    }

    public int getTextOutsideGravity() {
        return textOutsideGravity;
    }

    public void setTextOutsideGravity(int gravity) {
        this.textOutsideGravity = gravity;
        drawTextProgressPosition();
    }

    @Override
    public void onGlobalLayoutUpdated() {
        tvProgress.getComponentTreeObserver().removeTreeLayoutChangedListener(this);
        drawTextProgressPosition();
    }

    protected static class SavedState implements Sequenceable {
        int colorTextProgress;
        int textProgressSize;
        int textProgressMargin;

        String textProgress;

        int textInsideGravity;
        int textOutsideGravity;
        int textPositionPriority;

        @Override
        public boolean marshalling(Parcel out) {
            return out.writeFloat(this.colorTextProgress) &&
                    out.writeFloat(this.textProgressSize) &&
                    out.writeFloat(this.textProgressMargin) &&
                    out.writeString(this.textProgress) &&
                    out.writeInt(this.textInsideGravity) &&
                    out.writeInt(this.textOutsideGravity) &&
                    out.writeInt(this.textPositionPriority)  ;
        }

        @Override
        public boolean unmarshalling(Parcel in) {
            this.colorTextProgress = in.readInt();
            this.textProgressSize = in.readInt();
            this.textProgressMargin = in.readInt();
            this.textProgress = in.readString();
            this.textInsideGravity = in.readInt();
            this.textOutsideGravity = in.readInt();
            this.textPositionPriority = in.readInt();
            return true;
        }

        public static final Sequenceable.Producer PRODUCER = new Sequenceable.Producer() {

            @Override
            public SavedState createFromParcel(Parcel in) {
                SavedState instance = new SavedState();
                instance.unmarshalling(in);
                return instance;
            }
        };
    }
}
